package com.gitee.feizns.dynamic.io;

import com.gitee.feizns.dynamic.Strings;
import lombok.SneakyThrows;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

/**
 * 关于流处理的工具类
 * @author feizns
 * @since 2019/7/15
 */
public abstract class Ios {

    /**
     * 默认缓冲区大小
     */
    private static final int DEFAULT_BUFFER_SIZE = 4080;

    /**
     * 从文件系统读取
     * @param filepath FilePath 文件路径
     * @return {@link InputStream}
     */
    @SneakyThrows
    public static InputStream asInputStream(String filepath) {
        return clone(Files.newInputStream(Paths.get(filepath)));
    }

    /**
     * 从类路径中读取
     * @param clazz 类
     * @param classpath 路径
     * @return {@link InputStream}
     */
    @SneakyThrows
    public static InputStream asInputStream(Class<?> clazz, String classpath) {
        return clazz.getResourceAsStream(classpath);
    }

    /**
     * 读取流的内容并转成字符串
     * @param is {@link InputStream}
     * @return 字符数据 输入流中的数据并转化为{@link String}
     */
    public static String asString(InputStream is) {
        return new String(asBytes(is));
    }

    /**
     * 读取流的内容并转成字符串
     * @param filepath FilePath 文件路径
     * @return 字符数据 输入流中的数据并转化为{@link String}
     */
    public static String asString(String filepath) {
        return asString(asInputStream(filepath));
    }

    /**
     * 读取类路径中文件内容并转成字符串
     * @param classpath 类路径
     * @param clazz 类
     * @return 字符数据 {@link String}
     */
    public static String asString(Class<?> clazz, String classpath) {
        return asString(asInputStream(clazz, classpath));
    }

    /**
     * 读取流的内容到字节数组
     * @param is {@link InputStream}
     * @return 字节数据 输入流中的字节数据
     */
    public static byte[] asBytes(InputStream is) {
        try(ByteArrayOutputStream bao = new ByteArrayOutputStream()) {
            return transfer(is, bao).toByteArray();
        } catch (IOException e) {
            return new byte[]{};
        }
    }

    /**
     * 将流中的数据读取为并编码为Base64{@link String}
     * @param is 是否是
     * @return 字节数据 输入流中的字节数据
     */
    public static String asBase64(InputStream is) {
        return Base64.getEncoder().encodeToString(asBytes(is));
    }

    /**
     * 写入数据至输出流
     * @return {@link T}
     */
    @SneakyThrows
    public static <T extends OutputStream> T write(String string, T os) {
        return write(Strings.bytes(string), os);
    }

    /**
     * 写入数据至输出流
     * @return {@link T}
     */
    @SneakyThrows
    public static <T extends OutputStream> T write(byte[] bytes, T os) {
        os.write(bytes);
        os.flush();
        return os;
    }

    /**
     * 写入数据至输出流
     * @return {@link T}
     */
    @SneakyThrows
    public static <T extends OutputStream> T write(InputStream is, T os) {
        return transfer(is, os);
    }

    /**
     * 将输入流的内容复制多份
     * @param is {@link InputStream}
     * @param size 需要复制多少份流
     * @return 返回参数size份流的数组
     */
    public static InputStream[] clone(InputStream is, int size) throws IOException {
        InputStream[] ret = new InputStream[size];
        try ( ByteArrayOutputStream bao = new ByteArrayOutputStream() ) {
            Ios.transfer(is, bao);
            for (int i = 0; i < size; i++) {
                ret[i] = new ByteArrayInputStream(bao.toByteArray());
            }
        }
        return ret;
    }

    /**
     * 复制流
     * @param is 输入流
     * @return {@link InputStream}
     * @throws IOException io异常
     */
    public static InputStream clone(InputStream is) throws IOException {
        return clone(is, 1)[0];
    }

    /**
     * 复制流
     * @param is 输入流
     * @return {@link InputStream}
     * @throws IOException io异常
     */
    public static InputStream copy(InputStream is) throws IOException {
        return clone(is, 1)[0];
    }

    /**
     * 将输入流的内容转移到输出流
     * @param is 输入流
     * @param os 输出流
     * @param <T> 输出流的类型
     * @return 返回写入了输入流内容的输出流 os
     */
    public static <T extends OutputStream> T transfer(InputStream is, T os) throws IOException {
        int len;
        byte[] tmp = new byte[DEFAULT_BUFFER_SIZE];
        while ((len = is.read(tmp)) != -1) {
            os.write(tmp, 0, len);
        }
        os.flush();
        return os;
    }

}
